package impl

import (
	"apibe/single/src/v1/factory"
	"apibe/single/src/v1/share/crypt"
	"apibe/single/src/v1/share/db"
	"context"
	"encoding/json"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"time"
)

type User factory.User

func (u *User) UpsertOne(claims *crypt.CustomClaims) (*mongo.UpdateResult, error) {
	ctx := context.Background()
	opt := &options.UpdateOptions{}
	if u.ID == primitive.NilObjectID {
		u.ID = primitive.NewObjectID()
		u.CreateTime = time.Now()
		opt.SetUpsert(true)
	}
	one, err := db.GetMongoCollection(db.CollectionUser).UpdateOne(ctx, bson.M{"_id": u.ID, "account": u.Account}, bson.M{"$set": u}, opt)
	if err != nil {
		return nil, err
	}
	{ // 日志
		bytes, _ := json.Marshal(u)
		factory.OperationLogger(factory.Waring, u.ID, claims, fmt.Sprintf("User UpsertOne %v", string(bytes)))
	}
	return one, nil
}

func (u *User) AddLogger() {
	if u.ID == primitive.NilObjectID {
		return
	} else {
		logs, err := factory.OperationLoggerFindManyIds(u.ID)
		if err != nil {
			return
		}
		u.Logger = *logs
	}
}

func (*User) UpdateOne(filter map[string]interface{}, update map[string]interface{}, claims *crypt.CustomClaims) (*mongo.UpdateResult, error) {
	ctx := context.Background()
	one, err := db.GetMongoCollection(db.CollectionUser).UpdateOne(ctx, filter, update)
	if err != nil {
		return nil, err
	}
	{ // 日志
		bytes1, _ := json.Marshal(filter)
		bytes2, _ := json.Marshal(update)
		factory.OperationLogger(factory.Waring, one.UpsertedID, claims, fmt.Sprintf("User UpdateOne filter: %v update: %v", string(bytes1), string(bytes2)))
	}
	return one, nil
}

func (*User) UpdateMany(filter map[string]interface{}, update map[string]interface{}, claims *crypt.CustomClaims) (*mongo.UpdateResult, error) {
	ctx := context.Background()
	many, err := db.GetMongoCollection(db.CollectionUser).UpdateMany(ctx, filter, update)
	if err != nil {
		return nil, err
	}
	{
		// 日志收集
		filterBytes, _ := json.Marshal(filter)
		updateBytes, _ := json.Marshal(update)
		manyBytes, _ := json.Marshal(many)
		factory.OperationLogger(factory.Waring, many.UpsertedID, claims, fmt.Sprintf("Design UpdateMany filter: %v update: %v many: %v", string(filterBytes), string(updateBytes), string(manyBytes)))
	}
	return many, nil
}

func (*User) UpdateStatus(id primitive.ObjectID, claims *crypt.CustomClaims, status factory.Status) (*mongo.UpdateResult, error) {
	ctx := context.Background()
	update := bson.M{"$set": bson.M{"status": status}}
	result, err := db.GetMongoCollection(db.CollectionUser).UpdateByID(ctx, id, update)
	if err != nil {
		return nil, err
	}
	{
		// 日志搜集
		factory.OperationLogger(factory.Waring, result.UpsertedID, claims, fmt.Sprintf("User RemoveIdLogic RemoveId: %v", result.UpsertedID))
	}
	return result, err
}

func (*User) RemoveIdPhysics(id primitive.ObjectID, claims *crypt.CustomClaims) (*mongo.DeleteResult, error) {
	ctx := context.Background()
	update := make(map[string]interface{})
	update["_id"] = id
	var inD Design
	_ = db.GetMongoCollection(db.CollectionDesign).FindOne(ctx, bson.M{"_id": id}).Decode(&inD)
	one, err := db.GetMongoCollection(db.CollectionUser).DeleteOne(ctx, update)
	if err != nil {
		return nil, err
	}
	{
		bytes, _ := json.Marshal(inD)
		factory.OperationLogger(factory.Err, id, claims, fmt.Sprintf("Design RemoveIdPhysics RemoveId: %v Design: %v", id, string(bytes)))
	}
	return one, nil
}

func (*User) FindOneId(id primitive.ObjectID) *mongo.SingleResult {
	ctx := context.Background()
	result := db.GetMongoCollection(db.CollectionUser).FindOne(ctx, bson.M{"_id": id, "status": ignoreDeleted})
	return result
}
func (*User) FindOneFilter(filter map[string]interface{}) *mongo.SingleResult {
	ctx := context.Background()
	filter["status"] = ignoreDeleted
	result := db.GetMongoCollection(db.CollectionUser).FindOne(ctx, filter)
	return result
}
func (*User) FindManyFilter(filter map[string]interface{}, page int, pageSize int) (*mongo.Cursor, int64, error) {
	ctx := context.Background()
	opt := &options.FindOptions{}
	filter["status"] = ignoreDeleted
	count, err := db.GetMongoCollection(db.CollectionUser).CountDocuments(ctx, filter)
	if err != nil {
		return nil, 0, err
	}
	opt.SetSkip(int64((page - 1) * pageSize))
	opt.SetLimit(int64(pageSize))
	cursor, err := db.GetMongoCollection(db.CollectionUser).Find(ctx, filter, opt)
	if err != nil {
		return nil, 0, err
	}
	return cursor, count, nil
}

func (*User) Exist(filter map[string]interface{}) bool {
	ctx := context.Background()
	filter["status"] = ignoreDeleted
	err := db.GetMongoCollection(db.CollectionDesign).FindOne(ctx, filter).Err()
	if err != nil || err == mongo.ErrNoDocuments {
		return false
	}
	return true
}
